iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
Software Development

我的SpringBoot絕學:7+2個專案,從新手變專家系列 第 19

Day19 第六個Spring Boot專案:小型電商購物車系統(5)導入token驗證

  • 分享至 

  • xImage
  •  

我們花了很多的時間在處理token相關的設定,接下來我們要使用token通過Spring Security的驗證。

增加JWT的filter

首先要修改SecurityConfig.java,在csrf的下一行添加這些內容,讓Spring Security優先處理JWT認證。

在基本權限驗證的filter前,加上JWT認證的filter

                .addFilterBefore(new JWTAuthenticationFilter(), BasicAuthenticationFilter.class)

攔截帶有JWT的header用於驗證

接著,我們在Config下建立JWTAuthenticationFilter.java,讓它攔截帶有Authorization的header。

import Authentication時注意,選org.springframework才是我們用的,apache那個是錯誤的。

同一個request只會執行一次,避免重複執行

public class JWTAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

取得header中Authorization中的內容,裡面有JWT

String jwt = request.getHeader("Authorization");
if(jwt != null){

去除開頭的Bearer

我們的token傳輸採用Bearer token的標準,所以傳來的token的前7個字是”Bearer “,後面才是JWT的內容,需要去除這7個字,只留下JWT的部分。

因為substring是從0開始,所以-1。

jwt = jwt.substring("Bearer ".length() - 1);
            try{
                SecretKey key = Keys.hmacShaKeyFor(JWTConstant.SECRET != null ? JWTConstant.SECRET.getBytes() : null);
                

我們驗證JWT是否有效,有效就可以提供授權給這個用戶,無效或過期的JWT會發生例外,會跳到catch部分。

同時,解讀JWT中的payload區塊內容

 Claims claims = Jwts.parserBuilder()//建立JWT解讀器
                                    .setSigningKey(key)//設定檢驗的密鑰
                                    .build()//產生解讀器
                                    .parseClaimsJws(jwt)//解讀JWT
                                    .getBody();//取得payload內容

從JWT payload取得email

String email = String.valueOf(claims.get("email"));

有效的JWT就等於本人輸入正確的email和密碼,可以通過驗證

授權中帶有用戶的email,代表授權是只給這個用戶使用。

Authentication authentication = new UsernamePasswordAuthenticationToken(email, null, null);

設定通過Spring Security的認證

SecurityContextHolder.getContext().setAuthentication(authentication);
}

驗證時發現JWT無效或過期,就顯示錯誤

catch (Exception e){
                throw new BadCredentialsException("Invalid JWT or expired");
            }
        }

把request傳給下個filter使用

        filterChain.doFilter(request, response);
    }
}

使用JWT取得授權

我們來建立取得授權才能讀取的內容。

Controller

建立UserController.java,驗證通過可讀取登入的用戶資訊,只認JWT,沒JWT就算剛登入也不給過。

@RestController
@RequestMapping("/api/user")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/")
    public ResponseEntity<User> getUserInfo(@RequestHeader("Authorization") String jwt) throws Exception {
        User user = userService.findUserByJWT(jwt);
        return new ResponseEntity<>(user, HttpStatus.OK);
    }
}

Service

在UserService.java添加findUserByJWT,用JWT包含的email找用戶,別忘了添加用到的jwtProvider。

private final JWTProvider jwtProvider;

public UserService(/*skip*/, JWTProvider jwtProvider) {
        //skip
        this.jwtProvider = jwtProvider;
    }

public User findUserByJWT(String jwt) throws Exception{
        String email = jwtProvider.getEmailFromJWT(jwt);
        User user = userRepository.findByEmail(email);
        if(user == null){
            throw new Exception("Error: Invalid JWT");
        }
        return user;
    }

在JWTProvider.java,添加從jwt取得email的程式碼

public String getEmailFromJWT(String jwt) {
        jwt = jwt.substring("Bearer ".length() - 1);
        Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwt).getBody();
        return String.valueOf(claims.get("email"));
    }

測試

我們啟動專案來測試API

  1. 登入取得JWT
  2. 在Authorization選擇Bearer Token,把登入時取得的JWT貼上
  3. GET http://localhost:8080/api/user/

我們就能看到用戶的id、email和加密過的密碼。

我們剛才登入過,取消Authorization的Enabled後,送出請求。

發現如果沒有JWT,即使登入過還是會出現403錯誤。

我們常使用的網路服務都有採用token,最著名的就是Youtube。

有些Youtube頻道被盜用後,開啟直播進行比特幣詐騙,就是因為token被洩露造成的。

有了token,不用知道密碼就能取得用戶的資訊。

這點我們剛才也在專案體驗過了,所以大家要保護好自己的token,不要執行可疑的程式。


上一篇
Day18 第六個Spring Boot專案:小型電商購物車系統(4)註冊與登入
下一篇
Day20 第六個Spring Boot專案:小型電商購物車系統(6)商品與篩選功能
系列文
我的SpringBoot絕學:7+2個專案,從新手變專家31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言